using System;
using System.Collections.Generic;
using System.Text;

namespace Calc.TestHelpers
{
	public enum EquationNodeType
	{
		Constant,
		Variable,
		Function,
		AssignmentOp,
		AddOp,
		SubtractOp,
		MultiplyOp,
		DivideOp,
		ModuloOp,
		Parenthesese
	}

	public class EquationNode
	{
		protected EquationNode _left;
		protected EquationNode _right;

		protected EquationNodeType _type;

		protected List<EquationNode> _parameters;

		protected double _value;
		protected string _name;

		protected EquationNode(EquationNodeType type)
		{
			_type = type;
			_value = double.MinValue;
			_name = "";

			_left = null;
			_right = null;
			_parameters = new List<EquationNode>();
		}

		public EquationNode Left
		{
			get { return _left; }
		}

		public EquationNode Right
		{
			get { return _right; }
		}

		public EquationNodeType Type
		{
			get { return _type; }
		}

		public List<EquationNode> Parameters
		{
			get { return _parameters; }
		}

		public double Value
		{
			get { return _value; }
		}

		public string Name
		{
			get { return _name; }
		}

		#region Static Helper Methods
		public static EquationNode Add(EquationNode left, EquationNode right)
		{
			EquationNode node = new EquationNode(EquationNodeType.AddOp);
			node._left = left;
			node._right = right;

			return node;
		}

		public static EquationNode Subtract(EquationNode left, EquationNode right)
		{
			EquationNode node = new EquationNode(EquationNodeType.SubtractOp);
			node._left = left;
			node._right = right;

			return node;
		}

		public static EquationNode Multiply(EquationNode left, EquationNode right)
		{
			EquationNode node = new EquationNode(EquationNodeType.MultiplyOp);
			node._left = left;
			node._right = right;

			return node;
		}

		public static EquationNode Divide(EquationNode left, EquationNode right)
		{
			EquationNode node = new EquationNode(EquationNodeType.DivideOp);
			node._left = left;
			node._right = right;

			return node;
		}

		public static EquationNode Modulo(EquationNode left, EquationNode right)
		{
			EquationNode node = new EquationNode(EquationNodeType.ModuloOp);
			node._left = left;
			node._right = right;

			return node;
		}

		public static EquationNode Assignment(EquationNode left, EquationNode right)
		{
			EquationNode node = new EquationNode(EquationNodeType.AssignmentOp);
			node._left = left;
			node._right = right;

			return node;
		}

		public static EquationNode Constant(double value)
		{
			EquationNode node = new EquationNode(EquationNodeType.Constant);
			node._value = value;

			return node;
		}

		public static EquationNode Variable(string name)
		{
			EquationNode node = new EquationNode(EquationNodeType.Variable);
			node._name = name;

			return node;
		}

		public static EquationNode Function(string name, params string[] parameters)
		{
			EquationNode node = new EquationNode(EquationNodeType.Function);
			node._name = name;

			foreach ( string parm in parameters )
			{
				node.Parameters.Add(Variable(parm));
			}

			return node;
		}

		public static EquationNode Function(string name, params EquationNode[] nodes)
		{
			EquationNode node = new EquationNode(EquationNodeType.Function);
			node._name = name;

			foreach ( EquationNode snode in nodes )
			{
				node.Parameters.Add(snode);
			}

			return node;
		}

		public static EquationNode Parenthesise(EquationNode operation)
		{
			EquationNode node = new EquationNode(EquationNodeType.Parenthesese);
			node._left = operation;

			return node;
		}

		public static implicit operator EquationNode(double value)
		{
			return Constant(value);
		}

		public static implicit operator EquationNode(string name)
		{
			return Variable(name);
		}
		#endregion

		public override string ToString()
		{
			string op = "";
			switch ( _type )
			{
				case EquationNodeType.AddOp:
					op = "+";
					break;
				case EquationNodeType.AssignmentOp:
					op = "=";
					break;
				case EquationNodeType.DivideOp:
					op = "/";
					break;
				case EquationNodeType.ModuloOp:
					op = "%";
					break;
				case EquationNodeType.MultiplyOp:
					op = "*";
					break;
				case EquationNodeType.SubtractOp:
					op = "-";
					break;
				case EquationNodeType.Function:
					string func = _name;
					func += "(";
					bool first = true;
					foreach ( EquationNode node in _parameters )
					{
						if ( first )
							first = false;
						else
							func += ",";

						func += node.ToString();
					}

					func += ")";

					return func;
				case EquationNodeType.Constant:
					return _value.ToString();
				case EquationNodeType.Variable:
					return _name;
				case EquationNodeType.Parenthesese:
					string oper = "";
					if ( _left != null )
						oper = _left.ToString();

					return "(" + oper + ")";

			}

			string left = ( _left != null ? _left.ToString() : "" );
			string right = ( _right != null ? _right.ToString() : "" );

			return left + op + right;
		}
	}
}
